﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using KinectServer;
using System.Threading;
using System.Windows.Threading;


namespace KinectServer.Module
{
    /// <summary>
    /// 간단한 소켓 서버 (클라이언트 한개만 붙일수 있음)
    /// </summary>
    public abstract class TcpServer : IDisposable
    {
        private enum State
        {
            ACCEPTING,
            COMMUNICATING
        }
      
        private bool _isRunning;

        private Socket _server;
        private Thread _clientThread;
        private State _state;

        private Socket _client;

     
        public Action ServerClosed { get; set; }

        public int ClientCount { get; private set; }

        public TcpServer()
        {
            
        }

        //TODO: 소켓 서버 작성 및 동작 
        //TODO: 프로세싱 클라이언트 연동 
        //TODO: 영상 및 뎁스 전송 (프로토콜 고민)

        internal void Start()
        {
            if (this._isRunning == true) return;

            /* 초기화 시작 */
            try
            {
                this._clientThread = new Thread(new ThreadStart(this.Run));
                this._clientThread.Start();

            }
            catch (Exception ex)
            {
                Log.e(this.GetType(), "서버를 시작하지 못했습니다. - " + ex.Message);
            }

            /* 초기화 끝 */

            this._isRunning = true;
        }

        /// <summary>
        /// 클라이언트에게 데이터를 보냅니다.
        /// </summary>
        private void Run()
        {
            //서버 초기화
            this._server = new Socket(AddressFamily.InterNetwork,
                                       SocketType.Stream,
                                       ProtocolType.Tcp);

            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9100);
            this._server.Bind(ipep);

            Socket client = null;
            Action<byte[]> sendAction = p =>
            {
                if (p != null && p.Length > 0)
                {
                    client.Send(p);
                }
            };

            //메인 루프
            Log.i(this.GetType(), "서버가 시작되었습니다.");
            while (this._isRunning)
            {
                switch (this._state)
                {
                    case State.ACCEPTING:

                        try
                        {
                            Log.i(this.GetType(), "클라이언트가 접속하기를 기다립니다.");
                            this._server.Listen(10);

                            client = this._server.Accept();
                            Log.i(this.GetType(), "클라이언트가 한놈 들어왔습니다.");

                            this._client = client;
                            ++ClientCount;
                            this._state = State.COMMUNICATING;
                        }
                        catch (Exception ex)
                        {
                            System.Diagnostics.Debug.WriteLine(ex.ToString());
                            break;
                            //Log.e(this.GetType(), ex.Message);
                        }
                        break;

                    case State.COMMUNICATING:

                        if (client != null)
                        {
                            if (client.Connected == true)
                            {
                                try
                                {
                                    byte[] buffer = this.OnGetData();
                                    if (buffer != null && buffer.Length > 0)
                                    {
                                        client.Send(buffer);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Log.e(this.GetType(), "데이터를 보내는 중에 에러발생! - " + ex.Message);
                                    break;
                                }
                            }
                            else
                            {  
                                Log.i(this.GetType(), "클라이언트와의 연결이 끊어졌습니다. 집에 간듯...");
                                client.Close();
                                --ClientCount;
                                this._state = State.ACCEPTING;
                            }
                        }
                        else
                        {
                            Log.e(this.GetType(), "클라이언트가 생성이 안되었네요!");
                            break;
                        }
                        break;

                    default:
                        throw new InvalidOperationException();
                }
            }

            //서버 종료
            if (client != null)
            {
                client.Close();
            }          
            this.Stop();
            this.ClientCount = 0;
            Log.i(this.GetType(), "서버가 중단되었습니다.");
        }

        /// <summary>
        /// 서버에서 클라이언트로 전송할 데이터를 얻어옵니다.
        /// </summary>
        /// <returns></returns>
        protected abstract byte[] OnGetData();

        internal void Stop()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            //TODO: 연결 종료
            this._isRunning = false;
            this._server.Close();

            if (this.ServerClosed != null)
            {
                this.ServerClosed();
            }
        }

        internal void Send(byte[] p)
        {
            if (this._client != null)
            {
                if (this._client.Connected == true)
                {
                    this._client.Send(p);
                }
            }
        }
    }
}
